package org.tinygame.herostory.async;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinygame.herostory.MainThreadProcessor;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 异步操作
 * @author shkstart
 * @create 2022-05-08 11:05
 */
public class AsyncOperationProcessor {
    /**
     * 日志对象
     */
    private static final Logger LOGGER= LoggerFactory.getLogger(AsyncOperationProcessor.class);
    /**
     * 单例对象
     */
    private static final AsyncOperationProcessor _instance=new AsyncOperationProcessor();

    /**
     * 创建线程数组
     * 不要定义为static，会报错
     */
    private final ExecutorService[] _esArray=new ExecutorService[8];

    /**
     * 私有化默认构造器
     */
    private AsyncOperationProcessor(){
        for(int i=0;i<_esArray.length;i++){
            //线程名称
            final String threadName="AsyncOperationProcessor_"+i;
            //创建一个单线程
            _esArray[i]=Executors.newSingleThreadExecutor((newRunnable)-> {
                Thread newThread=new Thread(newRunnable);
                newThread.setName(threadName);
                return newThread;
            });
        }

    }

    /**
     * 获取单例对象
     * @return 异步操作处理器
     */
    public static AsyncOperationProcessor getInstance(){
        return _instance;
    }

    /**
     * 处理异步操作
     * @param asyncOp 异步操作
     */
    public void process(IAsyncOperation asyncOp){
        if(null==asyncOp){
            return;
        }


        //根据bindId获取索引
        int bindId=Math.abs(asyncOp.bindId());
        int esIndex=bindId % _esArray.length;

        //向异步线程中提交操作A和操作B，
        //操作A为asyncOp.doAsync();
        //操作B为MainThreadProcessor.GetInstance().process(asyncOp::doFinish);
        //只有当操作A被取出并执行完毕后，才会取出B并执行
        //而操作B是又提交了一个操作C到主线程中
        //这样就会使得操作A和操作C虽然是在两个线程中，但一定是严格的顺序执行
        //且操作A执行完成后才会执行操作C

        //MainThreadProcessor线程，将操作提交到AsyncOperationProcessor线程中等待执行
        _esArray[esIndex].submit(()->{
            try {
                asyncOp.doAsync();
                MainThreadProcessor.GetInstance().process(asyncOp::doFinish);
            } catch (Exception ex) {
                LOGGER.error(ex.getMessage(),ex);
            }
        });
    }
}
